namespace TwitterButtler
{
	#region #using Directives

	using System;
	using System.Collections.Generic;
	using System.Collections.ObjectModel;
	using System.ComponentModel.Composition;
	using System.Linq;
	using System.Windows.Threading;

	using Infrastructure;

	using Newtonsoft.Json;

	using TweetSharp;

	#endregion

	public class TimelineWatcher : IDisposable
	{
		private readonly DispatcherTimer twitterTimer;
		private DateTime lastCheck;
		private long lastId;

		public TimelineWatcher()
		{
			TimelineItems = new ObservableCollection<string>();

			this.lastCheck = DateTime.Now;

			this.twitterTimer = new DispatcherTimer(TimeSpan.FromHours(1.0 / 150.0), DispatcherPriority.ContextIdle, CheckForRequests,
			                                        Dispatcher.CurrentDispatcher);
		}

		#region Public Properties

		[ImportMany(typeof(IButtlerRequestServer))]
		public List<IButtlerRequestServer> ButtlerRequestServers { get; set; }

		public TwitterService TwitterService { get; set; }

		public ObservableCollection<string> TimelineItems { get; private set; }

		#endregion

		#region Implementation

		~TimelineWatcher()
		{
			Dispose(false);
		}

		private void CheckForRequests(object state, EventArgs e)
		{
			try
			{
				var directMessages = TwitterService.ListDirectMessagesReceivedSince(this.lastId);

				if (directMessages != null && directMessages.Any())
				{
					var directMessagesFiltered = from d in directMessages where d.CreatedDate.ToUniversalTime() > this.lastCheck.ToUniversalTime() orderby d.CreatedDate descending select d;

					this.lastId = directMessages.First().Id;

					foreach (var status in directMessagesFiltered)
					{
						PerformRequest(status.CreatedDate, status.Text, status.Sender.ScreenName, OnRequestCompleted);
					}
				}

				this.lastCheck = DateTime.Now;
			}
			catch (JsonReaderException)
			{
				// Ignore format errors
			}
		}

		private void OnRequestCompleted(string senderScreenName, string replyMessage)
		{
			// Reply with a Direct Message to the sender
			SendReply(senderScreenName, replyMessage);
		}

		private void SendReply(string senderScreenName, string replyMessage)
		{
			var reply = TwitterService.SendDirectMessage(senderScreenName, replyMessage);
		}

		private void PerformRequest(DateTime dateTime, string requestText, string senderScreenName, Action<string, string> onRequestCompleted)
		{
			TimelineItems.Insert(0, string.Format("{0}: @{2} -> {1}", dateTime.ToLocalTime(), requestText, senderScreenName));

			while (TimelineItems.Count > 20)
			{
				TimelineItems.RemoveAt(TimelineItems.Count - 1);
			}

			requestText = requestText.Trim();

			if (ButtlerRequestServers != null)
			{
				var requestServers = ButtlerRequestServers.Where(x => x.CanPerformRequest(senderScreenName, requestText));

				if (requestServers.Any())
				{
					foreach (var buttlerRequestServer in requestServers)
					{
						buttlerRequestServer.PerformRequest(dateTime, requestText, senderScreenName, onRequestCompleted);
					}
				}
				else
				{
					SendReply(senderScreenName, string.Format("I did not understand request '{0}'.", requestText));
				}
			}
		}

		#endregion

		#region Implementation of IDisposable

		/// <summary>
		/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
		/// </summary>
		/// <filterpriority>2</filterpriority>
		public void Dispose()
		{
			Dispose(true);
			GC.SuppressFinalize(this);
		}

		private void Dispose(bool disposing)
		{
			if (disposing)
			{
				this.twitterTimer.Stop();
			}
		}

		#endregion
	}
}